﻿using Acme.BookStore.Authors;
using Acme.BookStore.Permissions;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Logging;
using QRCoder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories;

namespace Acme.BookStore.Books
{
    [Authorize(BookStorePermissions.Books.Default)]
    public class BookAppService :
        CrudAppService<
            Book, //The Book entity
            BookDto, //Used to show books
            Guid, //Primary key of the book entity
            PagedAndSortedResultRequestDto, //Used for paging/sorting
            CreateUpdateBookDto>, //Used to create/update a book
        IBookAppService //implement the IBookAppService
    {
        private readonly IAuthorRepository _authorRepository;

        private readonly ILogger<BookAppService> _logger;

        public BookAppService(ILogger<BookAppService> logger,
            IRepository<Book, Guid> repository,
            IAuthorRepository authorRepository)
            : base(repository)
        {
            _logger = logger;
            _authorRepository = authorRepository;
            GetPolicyName = BookStorePermissions.Books.Default;
            GetListPolicyName = BookStorePermissions.Books.Default;
            CreatePolicyName = BookStorePermissions.Books.Create;
            UpdatePolicyName = BookStorePermissions.Books.Edit;
            DeletePolicyName = BookStorePermissions.Books.Create;
        }

        public override async Task<BookDto> GetAsync(Guid id)
        {
            //Get the IQueryable<Book> from the repository
            var queryable = await Repository.GetQueryableAsync();

            //Prepare a query to join books and authors
            var query = from book in queryable
                        join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
                        where book.Id == id
                        select new { book, author };

            //Execute the query and get the book with author
            var queryResult = await AsyncExecuter.FirstOrDefaultAsync(query);
            if (queryResult == null)
            {
                throw new EntityNotFoundException(typeof(Book), id);
            }

            var bookDto = ObjectMapper.Map<Book, BookDto>(queryResult.book);
            bookDto.AuthorName = queryResult.author.Name;
            return bookDto;
        }

        public override async Task<PagedResultDto<BookDto>> GetListAsync(PagedAndSortedResultRequestDto input)
        {
            _logger.LogError("_logger");
            Logger.LogError("hhh");
            //Get the IQueryable<Book> from the repository
            var queryable = await Repository.GetQueryableAsync();
            var authors = await _authorRepository.GetQueryableAsync();
            //Prepare a query to join books and authors
            var query = from book in queryable
                        join author in await _authorRepository.GetQueryableAsync() on book.AuthorId equals author.Id
                        select new { book, author };
            var cnm = queryable.Join(authors, x => x.AuthorId, y => y.Id, (x, y) => new { a = x, b = y });
            //Paging
            query = query
                .OrderBy(NormalizeSorting(input.Sorting))
                .Skip(input.SkipCount)
                .Take(input.MaxResultCount);

            //Execute the query and get a list
            var queryResult = await AsyncExecuter.ToListAsync(query);

            //Convert the query result to a list of BookDto objects
            var bookDtos = queryResult.Select(x =>
            {
                QRCodeGenerator qrGenerator = new();//https://192.168.3.40:44356/api/app/author/qrcode?name=ddd
                QRCodeData qrCodeData = qrGenerator.CreateQrCode($"https://localhost:44356/api/app/author/qrcode?name={x.book.Name}", QRCodeGenerator.ECCLevel.Q);

                #region Base64

                PngByteQRCode qrCode = new(qrCodeData);
                byte[] qrCodeAsPngByteArr = qrCode.GetGraphic(20);
                string qrCodeImageAsBase64 = Convert.ToBase64String(qrCodeAsPngByteArr);

                x.book.QrCode = qrCodeImageAsBase64;

                #endregion Base64

                #region SvgQRCode

                SvgQRCode svgQRCode = new(qrCodeData);
                string qrCodeAsSvg = svgQRCode.GetGraphic(2);
                //x.book.QrCode = qrCodeAsSvg;

                #endregion SvgQRCode

                #region BitmapByteQRCode

                //BitmapByteQRCode qrCode = new(qrCodeData);
                //byte[] qrCodeAsBitmapByteArr = qrCode.GetGraphic(2);

                ////From here on, you can implement your platform-dependent byte[]-to-image code
                ////e.g. Windows 10 - Full .NET Framework
                ////Bitmap qrCodeImage;
                ////using (var ms = new MemoryStream(qrCodeAsBitmapByteArr))
                ////{
                ////    qrCodeImage = new Bitmap(ms);
                ////}

                //var extension = ".jpeg";
                //string contentRootPath = _hostEnvironment.ContentRootPath;
                //var dt = $"{DateTime.Now:yyyyMMdd}";
                //var dir = Path.Combine(contentRootPath, $"wwwroot\\statics\\{dt}");
                //if (!Directory.Exists(dir))
                //{
                //    Directory.CreateDirectory(dir);
                //}
                //var file = Path.Combine(dir, $"{x.book.Name}{extension}");
                //var returnPath = $"\\statics\\{dt}\\{x.book.Name}{extension}";

                //File.WriteAllBytes(file, qrCodeAsBitmapByteArr);
                //x.book.QrCode = returnPath;

                #endregion BitmapByteQRCode

                var bookDto = ObjectMapper.Map<Book, BookDto>(x.book);
                bookDto.AuthorName = x.author.Name;
                bookDto.SvgQrCode = qrCodeAsSvg;

                return bookDto;
            }).ToList();

            //Get the total count with another query
            var totalCount = await Repository.GetCountAsync();

            return new PagedResultDto<BookDto>(
                totalCount,
                bookDtos
            );
        }

        public async Task<ListResultDto<AuthorLookupDto>> GetAuthorLookupAsync()
        {
            var authors = await _authorRepository.GetListAsync();

            return new ListResultDto<AuthorLookupDto>(
                ObjectMapper.Map<List<Author>, List<AuthorLookupDto>>(authors)
            );
        }

        private static string NormalizeSorting(string sorting)
        {
            if (sorting.IsNullOrEmpty())
            {
                return $"book.{nameof(Book.Name)}";
            }

            if (sorting.Contains("authorName", StringComparison.OrdinalIgnoreCase))
            {
                return sorting.Replace(
                    "authorName",
                    "author.Name",
                    StringComparison.OrdinalIgnoreCase
                );
            }

            return $"book.{sorting}";
        }
    }
}